Function.RegisterNamespace("xUnit.js.Console");

xUnit.js.Console.ScriptLoader=function(){
    // Private members
    var _attributeParser;

    var _events=null;
    var _defaultExtensions=['.js'];

    // Public Members
    this.Events;

    // ctor
    function ScriptLoader(){
        _attributeParser=new System.Script.Attributes.AttributeParser();
        this.Events=_events=new System.Event.EventManager("Loading","Success","Error","Loaded");
    };
    ScriptLoader.apply(this,arguments);
    
    // Public methods
    this.GetScriptList=function(pathList,extensions){
        if(!Object.IsType(Array,pathList))throw new Error("xUnit.js.Console.ScriptLoader.GetScriptList: 'pathList' must be an array of valid file or Directory paths.");
        if(!Object.IsType(Array,extensions))extensions=_defaultExtensions;
        var context={Extensions:extensions,ScriptList:[],Unknown:[]};
        Array.ForEach(pathList,addDirectory,directoryPredicate,context);
        Array.ForEach(pathList,addScript,scriptPredicate,context);
        if(context.Unknown.length)throw new Error(String.Format("xUnit.js.Console.ScriptLoader.GetScriptList: 'pathList' contained the following unknown paths: {0}",context.Unknown.join(', ')));
        return context.ScriptList;
    };
    
    this.LoadScripts=function(scriptList){
        if(!Object.IsType(Array,scriptList))throw new Error("xUnit.js.Console.ScriptLoader.LoadScripts: 'scriptList' must be an array of valid file paths.");
        Array.ForEach(scriptList,loadScript);
        _events.Fire("Loaded",{ScriptList:scriptList});
    };

    // Private methods
    function addDirectory(path,context){
        var directoryContext={Extensions:context.Extensions,ScriptList:context.ScriptList,Unknown:context.Unknown};
        var directories=System.IO.Directory.GetDirectories(path);
        if(directories)Array.ForEach(directories,addDirectory,null,directoryContext);
        var files=System.IO.Directory.GetFiles(path);
        if(files)Array.ForEach(files,addScript,scriptPredicate,directoryContext);
    }

    function addScript(path,context){
        context.ScriptList.push(path);
    }

    function loadScript(path,context){
        var filePath=System.IO.Path.GetFullPath(path);
        if(!System.IO.File.Exists(filePath))return;
        var root=System.IO.Path.GetRoot();
        System.IO.Path.SetRoot(System.IO.Path.GetPath(filePath));
        var timeStamp=new Date();
        try{
            _events.Fire("Loading",{Path:filePath});
            var scriptText=System.IO.File.GetFile(filePath);
            scriptText=_attributeParser.Parse(scriptText);
            scriptText=String.Format("{0}\n//@ sourceURL={1}",scriptText,filePath.replace(/\s/g,'_'));
            System.Script.ScriptLoader.Load(scriptText);
            _events.Fire("Success",{Path:filePath,Duration:new Date()-timeStamp});
        }catch(e){
            _events.Fire("Error",{Path:filePath,Duration:new Date()-timeStamp,Error:e});
        }
        System.IO.Path.SetRoot(root);
    }
    
    // Predicates
    function directoryPredicate(path,context){
        return path&&System.IO.Directory.Exists(path);
    }

    function scriptPredicate(path,context){
        if(path){
            for(var i=0;i<context.Extensions.length;i++){
                if(String.EndsWith(path,context.Extensions[i])){
                    if(directoryPredicate(path,context))continue;
                    if(System.IO.File.Exists(path))return true;
                }
            }
            if(!directoryPredicate(path)&&!System.IO.File.Exists(path)){
                context.Unknown.push(path);
            }
        }
        return false;
    }
};